﻿/*----------------------------------------------------------------
 * 版权所有 (c) 2025 HaiTangYunchi  保留所有权利
 * CLR版本：4.0.30319.42000
 * 公司名称：
 * 命名空间：TimeSyncTool
 * 唯一标识：b90e8792-8f73-4255-9bd6-1e710da8d60e
 * 文件名：Form1.cs
 * 
 * 创建者：海棠云螭
 * 电子邮箱：haitangyunchi@126.com
 * 创建时间：2025/4/30 21:26:54
 * 版本：V1.0.0
 * 描述：
 *
 * ----------------------------------------------------------------
 * 修改人：
 * 时间：
 * 修改说明：
 *
 * 版本：V1.0.1
 *----------------------------------------------------------------*/

using System;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using Timer = System.Windows.Forms.Timer;
using System.Threading.Tasks;
using System.Drawing;

namespace TimeSyncTool
{
    public partial class Form1 : Form
    {
        private Timer autoSyncTimer; // 自动同步定时器
        private bool isSyncing = false; // 同步状态标志

        // 常用NTP服务器列表
        private readonly string[] ntpServers = new string[]
        {
            "ntp.aliyun.com",        // 阿里云时间服务器
            "ntp.ntsc.ac.cn",        // 中国科学院国家授时中心
            "ntp.tencent.com",       // 腾讯云公共NTP
            "ntp.sjtu.edu.cn",       // 上海交通大学
            "cn.pool.ntp.org",       // 国内自动分配NTP池项目
            "ntp.cnnic.cn",          // CNNIC（中国互联网信息中心）
            "time.windows.com"       // 微软时间服务器
        };
        // Win32 API结构体，用于设置系统时间
        [StructLayout(LayoutKind.Sequential)]
        private struct SYSTEMTIME
        {
            public ushort wYear;         // 年
            public ushort wMonth;        // 月
            public ushort wDayOfWeek;    // 星期几(忽略)
            public ushort wDay;          // 日
            public ushort wHour;         // 时
            public ushort wMinute;       // 分
            public ushort wSecond;       // 秒
            public ushort wMilliseconds; // 毫秒
        }

        // 导入Win32 API函数，用于设置系统时间
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool SetSystemTime(ref SYSTEMTIME time);

        public Form1()
        {
            InitializeComponent();
            InitializeControls();    // 初始化控件
            InitializeTimer();       // 初始化定时器
        }

        /// <summary>
        /// 初始化控件状态和内容
        /// </summary>
        private void InitializeControls()
        {
            // 初始化NTP服务器下拉框
            cmbNtpServer.Items.AddRange(ntpServers);
            cmbNtpServer.SelectedIndex = 0;   // 默认选择第一个服务器

            // 初始化同步间隔数值框
            numSyncInterval.Value = 60;       // 默认60分钟
            numSyncInterval.Minimum = 1;      // 最小1分钟
            numSyncInterval.Maximum = 1440;   // 最大24小时(1440分钟)

            // 显示当前本地时间
            UpdateLocalTimeDisplay();
        }

        /// <summary>
        /// 初始化定时器
        /// </summary>
        private void InitializeTimer()
        {
            // 用于显示本地时间的定时器(每秒更新一次)
            Timer displayTimer = new Timer();
            displayTimer.Interval = 1000; // 1秒
            displayTimer.Tick += (s, e) => UpdateLocalTimeDisplay();
            displayTimer.Start();

            // 自动同步定时器
            autoSyncTimer = new Timer();
            autoSyncTimer.Interval = (int)numSyncInterval.Value * 60 * 1000; // 转换为毫秒
            autoSyncTimer.Tick += AutoSyncTimer_Tick;
        }

        /// <summary>
        /// 更新本地时间显示
        /// </summary>
        private void UpdateLocalTimeDisplay()
        {
            lblLocalTime.Text = $"本地时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}";
        }

        /// <summary>
        /// 手动同步按钮点击事件
        /// </summary>
        private void btnSync_Click(object sender, EventArgs e)
        {
            if (isSyncing)
            {
                UpdateStatus("请等待当前同步完成", true);
                return;
            }

            if (cmbNtpServer.SelectedItem == null)
            {
                UpdateStatus("请先选择NTP服务器", true);
                return;
            }

            string server = cmbNtpServer.SelectedItem.ToString();
            btnSync.Enabled = false;
            UpdateStatus($"开始与 {server} 同步...");

            Task.Run(() =>
            {
                try
                {
                    SyncTime(server);
                }
                finally
                {
                    this.Invoke((MethodInvoker)delegate { btnSync.Enabled = true; });
                }
            });
        }

        /// <summary>
        /// 同步时间方法
        /// </summary>
        /// <param name="ntpServer">NTP服务器地址</param>
        private void SyncTime(string ntpServer)
        {
            isSyncing = true;
            UpdateStatus($"正在与 {ntpServer} 同步...");

            try
            {
                using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)))
                {
                    var getTimeTask = Task.Run(() => GetNetworkTime(ntpServer), cts.Token);

                    if (getTimeTask.Wait(TimeSpan.FromSeconds(10)))
                    {
                        DateTime ntpTime = getTimeTask.Result;
                        SetSystemTime(ntpTime);
                        UpdateStatus($"同步成功: {ntpTime.ToLocalTime():yyyy-MM-dd HH:mm:ss}");
                        UpdateLocalTimeDisplay();
                    }
                    else
                    {
                        UpdateStatus("错误: 同步操作整体超时", true);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                UpdateStatus("警告: 同步操作已取消", true);
            }
            catch (TimeoutException ex)
            {
                UpdateStatus($"错误: {ex.Message}", true);
            }
            catch (Exception ex)
            {
                UpdateStatus($"同步失败: {ex.Message}", true);
            }
            finally
            {
                isSyncing = false;
            }
        }

        /// <summary>
        /// 从NTP服务器获取网络时间
        /// </summary>
        /// <param name="ntpServer">NTP服务器地址</param>
        /// <returns>获取到的网络时间</returns>
        private DateTime GetNetworkTime(string ntpServer)
        {
            const int timeoutMs = 5000; // 5秒超时
            var ntpData = new byte[48];
            ntpData[0] = 0x1B; // NTP协议版本

            /*
             * 模式	    版本	    值（十六进制）
             * Client	NTPv3	0x1B
             * Server	NTPv3	0x1C
             * Client	NTPv4	0x23
             * Server	NTPv4	0x24
             */

            try
            {
                var addresses = Dns.GetHostEntry(ntpServer).AddressList;
                var ipEndPoint = new IPEndPoint(addresses[0], 123);

                using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
                {
                    // 设置发送和接收超时
                    socket.SendTimeout = timeoutMs;
                    socket.ReceiveTimeout = timeoutMs;

                    // 异步连接和发送/接收
                    IAsyncResult connectResult = socket.BeginConnect(ipEndPoint, null, null);
                    if (!connectResult.AsyncWaitHandle.WaitOne(timeoutMs))
                    {
                        socket.Close();
                        UpdateStatus($"错误: 连接 {ntpServer} 超时", true);
                        throw new TimeoutException($"连接NTP服务器 {ntpServer} 超时");
                    }
                    socket.EndConnect(connectResult);

                    IAsyncResult sendResult = socket.BeginSend(ntpData, 0, ntpData.Length, SocketFlags.None, null, null);
                    if (!sendResult.AsyncWaitHandle.WaitOne(timeoutMs))
                    {
                        UpdateStatus($"错误: 发送请求到 {ntpServer} 超时", true);
                        throw new TimeoutException($"发送NTP请求到 {ntpServer} 超时");
                    }
                    socket.EndSend(sendResult);

                    IAsyncResult receiveResult = socket.BeginReceive(ntpData, 0, ntpData.Length, SocketFlags.None, null, null);
                    if (!receiveResult.AsyncWaitHandle.WaitOne(timeoutMs))
                    {
                        UpdateStatus($"错误: 从 {ntpServer} 接收响应超时", true);
                        throw new TimeoutException($"从 {ntpServer} 接收NTP响应超时");
                    }
                    socket.EndReceive(receiveResult);
                }

                // 解析NTP响应...
                const byte serverReplyTime = 40;
                uint intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
                uint fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);

                intPart = SwapEndianness(intPart);
                fractPart = SwapEndianness(fractPart);

                ulong milliseconds = ((ulong)intPart * 1000) + (((ulong)fractPart * 1000) / 0x100000000L);
                return new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)milliseconds);
            }
            catch (SocketException ex)
            {
                UpdateStatus($"网络错误: {ex.SocketErrorCode}", true);
                throw;
            }
            catch (Exception ex)
            {
                UpdateStatus($"NTP错误: {ex.Message}", true);
                throw;
            }
        }

        /// <summary>
        /// 交换32位无符号整数的字节序(大端转小端)
        /// </summary>
        private uint SwapEndianness(uint x)
        {
            return ((x & 0x000000ff) << 24) +
                   ((x & 0x0000ff00) << 8) +
                   ((x & 0x00ff0000) >> 8) +
                   ((x & 0xff000000) >> 24);
        }

        /// <summary>
        /// 设置系统时间
        /// </summary>
        /// <param name="newTime">要设置的新时间</param>
        private void SetSystemTime(DateTime newTime)
        {
            // 关键修复：当时时区正好差8小时，这样就能确保传入的时间是UTC时间
            DateTime utcTime = newTime.ToUniversalTime();

            var sysTime = new SYSTEMTIME
            {
                wYear = (ushort)utcTime.Year,
                wMonth = (ushort)utcTime.Month,
                wDay = (ushort)utcTime.Day,
                wHour = (ushort)utcTime.Hour,
                wMinute = (ushort)utcTime.Minute,
                wSecond = (ushort)utcTime.Second,
                wMilliseconds = (ushort)utcTime.Millisecond
            };

            // 调用Win32 API设置系统时间
            if (!SetSystemTime(ref sysTime))
            {
                UpdateStatus("设置系统时间失败，请以管理员身份运行此程序。", true);
                throw new Exception("设置系统时间失败，请以管理员身份运行此程序。");
            }
        }

        /// <summary>
        /// 更新状态栏信息
        /// </summary>
        /// <param name="message">要显示的状态信息</param>
        private void UpdateStatus(string message, bool isError = false)
        {
            this.Invoke((MethodInvoker)delegate
            {
                toolStripStatusLabel1.Text = message;
                toolStripStatusLabel1.ForeColor = isError ? Color.Red : Color.Green;//根据消息类型返回文字颜色，错误为红色，正常为绿色
            });
        }

        /// <summary>
        /// 自动同步复选框状态改变事件
        /// </summary>
        private void chkAutoSync_CheckedChanged(object sender, EventArgs e)
        {
            if (chkAutoSync.Checked)
            {
                // 启用自动同步
                autoSyncTimer.Interval = (int)numSyncInterval.Value * 60 * 1000;
                autoSyncTimer.Start();
                UpdateStatus($"自动同步已启用，每 {numSyncInterval.Value} 分钟同步一次");
            }
            else
            {
                // 禁用自动同步
                autoSyncTimer.Stop();
                UpdateStatus("自动同步已禁用");
            }
        }

        /// <summary>
        /// 同步间隔数值改变事件
        /// </summary>
        private void numSyncInterval_ValueChanged(object sender, EventArgs e)
        {
            if (chkAutoSync.Checked)
            {
                // 如果自动同步已启用，则更新定时器间隔
                autoSyncTimer.Interval = (int)numSyncInterval.Value * 60 * 1000;
                UpdateStatus($"同步间隔已更新为 {numSyncInterval.Value} 分钟");
            }
        }

        /// <summary>
        /// 自动同步定时器触发事件
        /// </summary>
        private void AutoSyncTimer_Tick(object sender, EventArgs e)
        {
            if (!isSyncing)
            {
                // 如果当前没有同步操作在进行，则开始新的同步
                ThreadPool.QueueUserWorkItem(state => SyncTime(cmbNtpServer.SelectedItem.ToString()));
            }
        }
    }
}